#! /bin/sh
# PCP QA Test No. 083
# Test pmlogger access control stuff and pmlc
#
# Copyright (c) 1995-2002 Silicon Graphics, Inc.  All Rights Reserved.
#

seq=`basename $0`
echo "QA output created by $seq"

# get standard environment, filters and checks
. ./common.product
. ./common.filter
. ./common.check

trap "rm -rf $tmp $tmp.*; exit" 0 1 2 3 15

# don't need to regenerate config.default with pmlogconf
#
export PMLOGGER_CHECK_SKIP_LOGCONF=yes

# real QA test starts here

echo "this tests access control for pmlogger and exercises pmlc"

# Remember that we cd into $tmp
#
here=`pwd`
signal=$PCP_BINADM_DIR/pmsignal
config=$tmp/pmlogger.conf
log=$tmp/pmlogger.log
me=`_get_fqdn`
shortme=`hostname`
errors=$tmp/errors.pmlc

echo "me=$me" >$here/$seq.full
echo "shortme=$shortme" >>$here/$seq.full

_filter()
{
    # for Linux sometimes see "Connection reset by peer" ... this is believed
    # to be a timing issue, and the results are semantically equivalent for
    # the purposes of this test, so ...
    # and the "receiving response from pmlogger" part of the no permission
    # error message may not be there for older pmlc versions, so ...
    #
    tee -a $here/$seq.full \
    | sed -e "s/$me/ME/" \
	-e "s/local:/ME/" \
	-e "s/$shortme/ME/" \
	-e "s/$other1/OTHER1/" \
	-e "s/$other2/OTHER2/" \
        -e 's/Connection reset by peer/Address already in use/' \
	-e '/No permission to perform/s/ receiving response from pmlogger//'
}

# Wait for appearance ($1 is true) or disappearance ($1 is false) of primary
# pmlogger
#
_await_logger()
{
    for i in 1 2 3 4 5
    do
	if $PCP_PS_PROG $PCP_PS_ALL_FLAGS | grep '[p]mlogger.*-P' | grep -v sudo >/dev/null
	then
	    # if it's there and we're waiting for it, break
	    $1 && break
	else
	    # if it's not there and we're waiting for it to disappear, break
	    $1 || break
	fi
	sleep 2
    done
    logger_pid=`$PCP_PS_PROG $PCP_PS_ALL_FLAGS | grep '[p]mlogger.*-P' | grep -v sudo | $PCP_AWK_PROG '{ print $2 }'`
    if [ ! -z "$logger_pid" ]
    then
	# it's alive...
	if $1
	then
	    echo "primary pmlogger alive"
	else
	    # ...but meant to die
	    echo
	    echo "primary pmlogger won't die, can't do QA test, ...giving up!"
	    $PCP_PS_PROG $PCP_PS_ALL_FLAGS | egrep '[p]m|[P]PID'
	    exit 1
	fi
    else
	# it's not alive...
	if $1
	then
	    # ...but should be
	    echo
	    echo "primary pmlogger won't start, can't do QA test, ...giving up!"
	    echo "pmlogger log file:"
	    cat $log
	    echo "pmlogger sh log:"
	    cat $tmp/sh.log
	    $PCP_PS_PROG $PCP_PS_ALL_FLAGS | egrep '[p]m|[P]PID'
	    exit 1
	else
	    echo "primary pmlogger terminated"
	fi
    fi
}

interrupt()
{
    echo "Interrupted"
    _restore_auto_restart pmcd
    _restore_auto_restart pmlogger
    exit
}

cleanup()
{
    cd $here
    # kill off any surviving pmloggers
    #
    $sudo $signal -a -s TERM pmlogger
    _await_logger false
    echo "Restarting pmcd and friends..."
    _service pcp restart 2>&1 \
    | _filter_pcp_start \
    | sed -e '/pmlogger not running/d'
    _restore_auto_restart pmcd
    _restore_auto_restart pmlogger
    _wait_for_pmcd
    echo "+++ after restart primary pmlogger pid = `_get_primary_logger_pid`" >>$here/$seq.full
    case `hostname -s`
    in
	vm10|vm32)
	    # on these VMs we consistently don't get pmlogger started in the
	    # default 20 seconds, so be patient ...
	    #
	    sleep 30
	    ;;
    esac
    _wait_for_pmlogger
    rm -rf $tmp $tmp.*
}

trap interrupt 1 2 3 15
trap cleanup 0

_stop_auto_restart pmcd
_stop_auto_restart pmlogger

# real QA test starts here

echo
echo "Running the access tests"

# ideally want a host with only 1 network interface ... getpmcdhosts
# cannot express this, so we used to go for 1 CPU as a likely co-condition,
# but even that has been dropped now that single CPU systems are so
# rare
#
# Need at least PCP 3.0 here, because there is an interoperability
# issue with older pmlc versions (e.g. 2.7.8) talking to a newer
# pmlogger ... the pmlc-pmlogger channel is fine, but when pmlc tries
# to contact the pmlogger's pmcd, if the pmlogger is using a local
# (Unix domain) socket, :local, then the wheels fall off.
#
eval `./getpmcdhosts -L -n2 -v 'pcp>=3' | sed -e 's/^/other1=/' -e 's/ / other2=/'`
if [ -z "$other1" ]
then
    _notrun "Cannot find first remote host running pmcd v2.x"
    # NOTREACHED
fi
if [ -z "$other2" ]
then
    _notrun "Cannot find second remote host running pmcd v2.x"
    # NOTREACHED
fi
echo "other1=$other1" >>$here/$seq.full
echo "other2=$other2" >>$here/$seq.full

# kill off any existing primary pmlogger
#
$sudo $signal -a -s TERM pmlogger
_await_logger false

if mkdir $tmp
then
    cd $tmp
    # since we now run pmlogger as user/group pcp/pcp when -P is
    # specified, need to make this directory writeable
    #
    chmod 777 .
else
    echo "Unable to create working directory $tmp, ...giving up!"
    exit 1
fi

cat >$config <<End-Of-File
# test various "all" commands
log mandatory on 1 hour {
    kernel.all.load [ "1 minute" ]
}

log mandatory maybe sample.bin["bin-100","bin-200","bin-300","bin-400"]
log mandatory off   sample.bin["bin-600","bin-700","bin-800","bin-900"]

[access]
allow $other1 :		all except mandatory;
disallow $other2 :	all;
allow $me :		all;
allow localhost :	all;
End-Of-File

echo >>$here/$seq.full
cat $config >>$here/$seq.full

echo "starting test pmlogger..."
#
# extra parentheses and I/O redirection so that shell doesn't print pid
#
( sh -c "$sudo $PCP_BINADM_DIR/pmlogger -P -c $config -l $log TEST" >$tmp/sh.log 2>&1 & ) >/dev/null 2>&1
_await_logger true
_wait_for_pmlogger $logger_pid $log

# Check connect and enquire access

echo | tee -a $here/$seq.full
echo "================" | tee -a $here/$seq.full
echo "checking enquire access for this host..." | tee -a $here/$seq.full
pmlc -P <<End-Of-File 2>$errors \
| _filter
query kernel.all.load[1,5,15]
quit
End-Of-File
if [ -s $errors ]
then
    echo Errors: | tee -a $here/$seq.full
    _filter < $errors
    $PCP_PS_PROG $PCP_PS_ALL_FLAGS | egrep '[p]m|[P]PID'
    exit 1
fi


echo | tee -a $here/$seq.full
echo "================" | tee -a $here/$seq.full
echo "checking enquire access for other1..." | tee -a $here/$seq.full
ssh -q pcpqa@$other1 "sh -c 'PMCD_CONNECT_TIMEOUT=60 PMCD_REQUEST_TIMEOUT=60 pmlc -h $me -P'" <<End-Of-File 2>$errors \
| _filter
query kernel.all.load[1,5,15]
quit
End-Of-File
if [ -s $errors ]
then
    echo Errors: | tee -a $here/$seq.full
    _filter < $errors
    echo FAILED ... sleeping
    exit 1
fi

echo | tee -a $here/$seq.full
echo "================" | tee -a $here/$seq.full
echo "checking enquire access for other2 (should fail)..." | tee -a $here/$seq.full
ssh -q pcpqa@$other2 "sh -c 'PMCD_CONNECT_TIMEOUT=60 PMCD_REQUEST_TIMEOUT=60 pmlc -h $me -P'" <<End-Of-File 2>$errors \
| _filter
query kernel.all.load[1,5,15]
quit
End-Of-File
if [ -s $errors ]
then
    echo Errors: | tee -a $here/$seq.full
    _filter < $errors
fi

echo | tee -a $here/$seq.full
echo "================" | tee -a $here/$seq.full
echo "re-checking enquire access for this host..." | tee -a $here/$seq.full
pmlc -P <<End-Of-File 2>$errors \
| _filter
query kernel.all.load[1,5,15]
quit
End-Of-File
if [ -s $errors ]
then
    echo Errors: | tee -a $here/$seq.full
    _filter < $errors
    exit 1
fi

# Now check advisory access using sample.bin

cat >$config <<End-Of-File
# test various explicit access commands

log mandatory maybe sample.bin["bin-100","bin-200","bin-300"]
log mandatory off   sample.bin["bin-700","bin-800","bin-900"]

[access]
allow $me :		enquire, advisory, mandatory;
allow localhost :	enquire, advisory, mandatory;
allow $other1 :		enquire, advisory;
allow $other2 :		enquire;
disallow *: all;
End-Of-File

echo >>$here/$seq.full
cat $config >>$here/$seq.full

echo "killing pmlogger used for enquire tests..."
$sudo $signal -a -s TERM pmlogger
_await_logger false

echo
echo "starting new pmlogger for advisory & mandatory tests..."
#
# extra parentheses and I/O redirection so that shell doesn't print pid
#
( sh -c "$sudo $PCP_BINADM_DIR/pmlogger -P -c $config -l $log TEST2" >$tmp/sh.log 2>&1 & ) >/dev/null 2>&1
_await_logger true
_wait_for_pmlogger $logger_pid $log

echo | tee -a $seq.full
echo "================" | tee -a $seq.full
echo "checking advisory access for this host..." | tee -a $seq.full
echo "(100,400 will change, 700 will not)" | tee -a $seq.full
pmlc -P <<End-Of-File 2>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
advisory on 1 hour sample.bin[100]
advisory on 2 hour sample.bin[400]
advisory on 3 hour sample.bin[700]
quit
End-Of-File
pmsleep 0.1
pmlc -P <<End-Of-File 2>>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
quit
End-Of-File
if [ -s $errors ]
then
    $PCP_PS_PROG $PCP_PS_ALL_FLAGS | grep pmlc
    cat $log
    echo Errors: | tee -a $seq.full
    _filter < $errors
    echo "pmlogger sh log:"
    cat $tmp/sh.log
    exit 1
fi

echo | tee -a $seq.full
echo "================" | tee -a $seq.full
echo "checking advisory access for other1..." | tee -a $seq.full
echo "(200,500 will change, 800 will not)" | tee -a $seq.full
ssh -q pcpqa@$other1 "sh -c 'PMCD_CONNECT_TIMEOUT=60 PMCD_REQUEST_TIMEOUT=60 pmlc -h $me -P'" <<End-Of-File 2>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
advisory on 1 hour sample.bin[200]
advisory on 2 hour sample.bin[500]
advisory on 3 hour sample.bin[800]
quit
End-Of-File
pmsleep 0.1
pmlc -P <<End-Of-File 2>>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
quit
End-Of-File
if [ -s $errors ]
then
    echo Errors: | tee -a $seq.full
    _filter < $errors
fi

echo | tee -a $seq.full
echo "================" | tee -a $seq.full
echo "checking advisory access for other2..." | tee -a $seq.full
echo "(expect 3 permission errors)" | tee -a $seq.full
ssh -q pcpqa@$other2 "sh -c 'PMCD_CONNECT_TIMEOUT=60 PMCD_REQUEST_TIMEOUT=60 pmlc -h $me -P'" <<End-Of-File 2>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
advisory on 1 hour sample.bin[300]
advisory on 2 hour sample.bin[600]
advisory on 3 hour sample.bin[900]
quit
End-Of-File
pmsleep 0.1
pmlc -P <<End-Of-File 2>>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
quit
End-Of-File
if [ -s $errors ]
then
    echo Errors: | tee -a $seq.full
    _filter < $errors
fi

# Now check mandatory access using sample.bin (same config file as for advisory
# access)

echo "killing pmlogger used for advisory tests..."
$sudo $signal -a -s TERM pmlogger
_await_logger false

echo
echo "starting new pmlogger for mandatory tests..."
#
# extra parentheses and I/O redirection so that shell doesn't print pid
#
( sh -c "$sudo $PCP_BINADM_DIR/pmlogger -P -c $config -l $log TEST3" >$tmp/sh.log 2>&1 & ) >/dev/null 2>&1
_await_logger true
_wait_for_pmlogger $logger_pid $log

echo | tee -a $seq.full
echo "================" | tee -a $seq.full
echo "checking mandatory access for this host..." | tee -a $seq.full
echo "(100,400,700 will change)" | tee -a $seq.full
pmlc -P <<End-Of-File 2>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
mandatory on 3 hour sample.bin[100]
mandatory on 4 hour sample.bin[400]
mandatory on 5 hour sample.bin[700]
quit
End-Of-File
pmsleep 0.1
pmlc -P <<End-Of-File 2>>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
quit
End-Of-File
if [ -s $errors ]
then
    echo Errors: | tee -a $seq.full
    _filter < $errors
fi

echo | tee -a $seq.full
echo "================" | tee -a $seq.full
echo "checking mandatory access for other1..." | tee -a $seq.full
echo "Expect 3 permission errors" | tee -a $seq.full
ssh -q pcpqa@$other1 "sh -c 'PMCD_CONNECT_TIMEOUT=60 PMCD_REQUEST_TIMEOUT=60 pmlc -h $me -P'" <<End-Of-File 2>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
mandatory on 3 hour sample.bin[200]
mandatory on 4 hour sample.bin[500]
mandatory on 5 hour sample.bin[800]
quit
End-Of-File
pmsleep 0.1
pmlc -P <<End-Of-File 2>>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
quit
End-Of-File
if [ -s $errors ]
then
    echo Errors: | tee -a $seq.full
    _filter < $errors
fi

echo | tee -a $seq.full
echo "================" | tee -a $seq.full
echo "checking mandatory access for other2..." | tee -a $seq.full
echo "Expect 3 permission errors" | tee -a $seq.full
ssh -q pcpqa@$other2 "sh -c 'PMCD_CONNECT_TIMEOUT=60 PMCD_REQUEST_TIMEOUT=60 pmlc -h $me -P'" <<End-Of-File 2>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
mandatory on 3 hour sample.bin[300]
mandatory on 4 hour sample.bin[600]
mandatory on 5 hour sample.bin[900]
quit
End-Of-File
pmsleep 0.1
pmlc -P <<End-Of-File 2>>$errors \
| _filter
query sample.bin[100,200,300,400,500,600,700,800,900]
quit
End-Of-File
if [ -s $errors ]
then
    echo Errors: | tee -a $seq.full
    _filter < $errors
fi

# Check that each pmlogger only accepts one pmlc connection at a time

echo | tee -a $seq.full
echo "================" | tee -a $seq.full
echo "checking 2nd pmlc for pmlogger (should fail)..." | tee -a $seq.full
( ( sleep 5 | pmlc -P ) & ) >/dev/null 2>&1
sleep 3
pmlc -P <<End-Of-File 2>$errors \
| _filter
quit
End-Of-File
if [ -s $errors ]
then
    echo Errors: | tee -a $seq.full
    _filter < $errors
fi

wait

echo "+++ at the end primary pmlogger pid = `_get_primary_logger_pid`" >>$here/$seq.full
